home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 19 / wxmopas.zip / XFER.INC < prev   
Text File  |  1986-09-16  |  37KB  |  924 lines

  1.   {   -TurboPascal: Checksum Xmodem, CRC Xmodem, WXmodem 7/86
  2. Peter Boswell
  3. ADI
  4. Suite 650
  5. 350 N. Clark St.
  6. Chicago, Il 60610
  7. People/Link: Topper
  8. Compuserve:  72247,3671
  9. Daytime Phone (312) 670-2660
  10.   }
  11.  
  12. {The following support routines are not provided here:
  13.  
  14.         SHOWCRT         -  Display download status
  15.         OPENTEMP        -  Open temporary display window
  16.         CLOSETEMP       -  Close temporary display window
  17.         STATUS          -  Display download status
  18.         CGETC           -  Get a character from the UART
  19.         SEND            -  Send a character through the UART
  20.         UPDATE_UART     -  Set parity, data bits
  21.         }
  22. const
  23.      SOH = 1;                          {Start Of Header}
  24.      EOT = 4;                          {End Of Transmission}
  25.      ACK = 6;                          {ACKnowledge}
  26.      DLE = $10;                        {Data Link Escape}
  27.      XON = $11;                        {X-On}
  28.      XOFF = $13;                       {X-Off}
  29.      NAK = $15;                        {Negative AcKnowledge}
  30.      SYN = $16;                        {Synchronize}
  31.      CAN = $18;                        {CANcel}
  32.      CHARC = $43;                      {C = CRC Xmodem}
  33.      CHARW = $57;                      {W = WXmodem}
  34.      MAXERRS = 10;                     {Maximum allowed errors}
  35.      L = 0;
  36.      H = 1;
  37.      Buflen  = 128;                    {Disk I/O buffer length}
  38.      Bufnum = 64;                      {Disk I/O buffer count}
  39.      Maxwindow = 4;                    {Wxmodem window size}
  40.                                        {CRC byte translation table}
  41.      Crctab: array[0..255] of Integer =
  42.      (0, 4129, 8258, 12387, 16516, 20645, 24774, 28903,
  43.       -32504,-28375,-24246,-20117,-15988,-11859,-7730,-3601,
  44.       4657, 528, 12915, 8786, 21173, 17044, 29431, 25302,
  45.       -27847,-31976,-19589,-23718,-11331,-15460,-3073,-7202,
  46.       9314, 13379, 1056, 5121, 25830, 29895, 17572, 21637,
  47.       -23190,-19125,-31448,-27383,-6674,-2609,-14932,-10867,
  48.       13907, 9842, 5649, 1584, 30423, 26358, 22165, 18100,
  49.       -18597,-22662,-26855,-30920,-2081,-6146,-10339,-14404,
  50.       18628, 22757, 26758, 30887, 2112, 6241, 10242, 14371,
  51.       -13876,-9747,-5746,-1617,-30392,-26263,-22262,-18133,
  52.       23285, 19156, 31415, 27286, 6769, 2640, 14899, 10770,
  53.       -9219,-13348,-1089,-5218,-25735,-29864,-17605,-21734,
  54.       27814, 31879, 19684, 23749, 11298, 15363, 3168, 7233,
  55.       -4690,-625,-12820,-8755,-21206,-17141,-29336,-25271,
  56.       32407, 28342, 24277, 20212, 15891, 11826, 7761, 3696,
  57.       -97,-4162,-8227,-12292,-16613,-20678,-24743,-28808,
  58.       -28280,-32343,-20022,-24085,-12020,-16083,-3762,-7825,
  59.       4224, 161, 12482, 8419, 20484, 16421, 28742, 24679,
  60.       -31815,-27752,-23557,-19494,-15555,-11492,-7297,-3234,
  61.       689, 4752, 8947, 13010, 16949, 21012, 25207, 29270,
  62.       -18966,-23093,-27224,-31351,-2706,-6833,-10964,-15091,
  63.       13538, 9411, 5280, 1153, 29798, 25671, 21540, 17413,
  64.       -22565,-18438,-30823,-26696,-6305,-2178,-14563,-10436,
  65.       9939, 14066, 1681, 5808, 26199, 30326, 17941, 22068,
  66.       -9908,-13971,-1778,-5841,-26168,-30231,-18038,-22101,
  67.       22596, 18533, 30726, 26663, 6336, 2273, 14466, 10403,
  68.       -13443,-9380,-5313,-1250,-29703,-25640,-21573,-17510,
  69.       19061, 23124, 27191, 31254, 2801, 6864, 10931, 14994,
  70.       -722,-4849,-8852,-12979,-16982,-21109,-25112,-29239,
  71.       31782, 27655, 23652, 19525, 15522, 11395, 7392, 3265,
  72.       -4321,-194,-12451,-8324,-20581,-16454,-28711,-24584,
  73.       28183, 32310, 20053, 24180, 11923, 16050, 3793, 7920);
  74.  
  75. {*** variables used as globals in this source segment
  76.      (actually global to whole  source) ***}
  77. var
  78.    checksum     : integer;
  79.    fname        : bigstring;
  80.    response     : string[1];
  81.    crcval,db,sb : integer;
  82.    packetln     : integer;            {128 + Checksum or 128 + CRC}
  83.    p            : parity_set;
  84.    dbuffer      : array[1..Bufnum,1..Buflen] of byte;
  85.    dcount       : integer;
  86.    Wxmode       : boolean;
  87.    Crcmode      : boolean;
  88.    Openflag     : boolean;
  89.  
  90. procedure updcrc(a : byte);  {Calculate CRC}
  91. begin
  92.   {
  93.      crcval := Crctab[hi(crcval) xor a] xor (lo(crcval) shl 8);
  94.   }
  95.      inline(              {assembler equivalent of above Pascal statement}
  96.  
  97.         $A1/crcval/       {mov ax,crcval     AX <- crcval}
  98.         $89/$C2/          {mov dx,ax         DX <- crcval}
  99.         $88/$E0/          {mov al,ah         (AX) crcval >> 8}
  100.         $B4/$00/          {mov ah,0 }
  101.         $36/              {ss:}
  102.         $8B/$8E/a/        {mov cx,[bp+a]     CX <- a}
  103.         $31/$C8/          {xor ax,cx         AX <- (crcval >> 8) xor a}
  104.         $D1/$E0/          {shl ax,1          AX <- AX * 2  (word index)}
  105.         $BB/crctab/       {mov bx,offset crctab   BX <- addr(crctab)}
  106.         $01/$C3/          {add bx,ax         BX <- addr(crctab)+((crcval>>8)xor a)*2 }
  107.         $2E/              {cs:}
  108.         $8B/07/           {mov ax,[bx]       AX <- contents of crctab}
  109.         $88/$D6/          {mov dh,dl         (DX) crcval << 8}
  110.         $B2/$00/          {mov dl,00}
  111.         $31/$D0/          {xor ax,dx         AX <- contents of crctab xor crcval << 8}
  112.         $A3/crcval        {mov crcval,ax     crcval <- AX}
  113.  
  114.           );
  115. end;
  116.  
  117. { Xmodem transmit window routine
  118.   Peter Boswell, July 1986       }
  119.  
  120. procedure txwindow(opt : integer; in_string : bigstring);
  121.  
  122. begin
  123.    case opt of
  124.        1  :     begin                           {initialize}
  125.                    OpenTemp(36,3,78,18,2);
  126.                    Clrscr;
  127.                    GotoXY(10,1);
  128.                    write('File - ',in_string);
  129.                    GotoXY(10,2);
  130.                    write('Mode -');
  131.                    GotoXY(4,3);
  132.                    write('Total time -');
  133.                    GotoXY(2,4);
  134.                    write('Total Blocks -');
  135.                    GotoXY(10,5);
  136.                    write('Sent -');
  137.                    GotoXY(9,6);
  138.                    write('ACK''d -');
  139.                    GotoXY(6,7);
  140.                    write('Last NAK -');
  141.                    GotoXY(9,8);
  142.                    write('X-Off - No');
  143.                    GotoXY(8,9);
  144.                    write('Window - 0');
  145.                    GotoXY(4,11);
  146.                    write('Last Error -');
  147.                    GotoXY(8,10);
  148.                    write('Errors -');
  149.                 end;
  150.        2..11  : begin
  151.                    GotoXY(17,opt);
  152.                    ClrEol;
  153.                    write(in_string);
  154.                 end;
  155.        12     : begin
  156.                    GotoXY(3,12);
  157.                    ClrEol;
  158.                    write(in_string);
  159.                 end;
  160.        99     : CloseTemp;
  161.    end;                                         {case}
  162. end;
  163. {
  164.   This routine deletes all DLE characters and XOR's the following character
  165.   with 64.  If a SYN character is found then -2 is returned.
  166.     }
  167. function dlecgetc(Tlimit : integer) : integer;
  168. var
  169. savecgetc : integer;
  170. begin
  171.      if wxmode then
  172.      begin
  173.           savecgetc := cgetc(Tlimit);
  174.           if savecgetc = SYN then
  175.              savecgetc := -2
  176.           else
  177.           if savecgetc = DLE then
  178.           begin
  179.                savecgetc := cgetc(Tlimit);
  180.                if savecgetc >= 0 then savecgetc := savecgetc XOR 64;
  181.           end;
  182.           dlecgetc := savecgetc;
  183.      end
  184.      else
  185.      dlecgetc := cgetc(Tlimit);
  186. end;
  187.  
  188. procedure purge;
  189. begin
  190.      while cgetc(1) <> -1 do
  191.            ;
  192. end;
  193. procedure SaveCommStatus;
  194. begin
  195.       p := parity;
  196.       db := dbits;
  197.       sb := stop_bits;
  198.       dbits        := 8;
  199.       parity       := none;
  200.       stop_bits    := 1;
  201.       update_uart
  202. end;
  203.  
  204. procedure recv_wcp;
  205. {receive a file using Ward Christensen's checksum protocol,CRC Xmodem, WXmodem}
  206. label
  207.      99;
  208. var
  209.    j, firstchar, sectnum, sectcurr, prevchar, lignore, blkcnt,
  210.    toterr, errors, sectcomp, bufcurr, bresult : integer;
  211.    Xtrace, EotFlag, ErrorFlag : boolean;
  212.    UserKey : char;
  213.    blkfile : file;
  214. begin
  215.      status(2, 'RECV XMODEM');
  216.      ErrorFlag := TRUE;
  217.      EotFlag   := False;
  218.      Xtrace    := False;
  219.      Openflag  := False;
  220.      Bufcurr   := 1;
  221.      SaveCommStatus;
  222.      While ErrorFlag do
  223.      begin
  224.           OpenTemp(1,3,80,8,2);
  225.           repeat
  226.                 write('Enter a filename for download file (<cr> to abort): ');
  227.                 readln(fname);
  228.                 supcase(fname);
  229.                 if length(fname) > 0 then
  230.                    if exists(fname) then
  231.                    begin
  232.                      write(fname, ' Exists. OK to overwrite it (Y/N)? ');
  233.                      readln(response);
  234.                      if upcase(response) = 'Y' then
  235.                         ErrorFlag := FALSE;
  236.                    end
  237.                    else ErrorFlag := FALSE
  238.           until (not ErrorFlag) or (length(fname) = 0);
  239.           CloseTemp;
  240.           if length(fname) > 0 then
  241.           begin
  242.                Assign(blkfile,fname);
  243.                {$I-} Rewrite(blkfile); {$I+}
  244.                ErrorFlag := (IOresult <> 0);
  245.                if ErrorFlag then
  246.                begin
  247.                   writeln(#13,#10,'ITERM --- cannot open file');
  248.                   goto 99;
  249.                end
  250.                else
  251.                   openflag := True;
  252.           end;
  253.           if length(fname) = 0 then
  254.           begin
  255.                writeln(#13,#10,'ITERM --- user aborted receive.');
  256.                goto 99;
  257.           end;
  258.      end;                                       {while}
  259.      writeln('Ready to receive ', fname);
  260.      blkcnt := 0;
  261.      sectnum := 0;
  262.      errors := 0;
  263.      toterr := 0;
  264.      ShowCrt(0,0,0);
  265.      Crcmode  := true;                          {Assume CRC versus Checksum}
  266.      Packetln := 130;                           {128 byte data + 2 byte CRC}
  267.      Wxmode   := true;                          {Assume Wxmodem}
  268.      Lignore  := 0;                             {ignore packets after error}
  269.      i:=0;                                      {Try for Wxmodem 3 times}
  270.      purge;
  271.      writeln('Trying Wxmodem');
  272.      repeat
  273.           send(ord('W'));
  274.           firstchar := cgetc(12);               {12 seconds each}
  275.           if keypressed then
  276.           begin
  277.                read(kbd,UserKey);
  278.                if UserKey = ^X then goto 99
  279.           end;
  280.           i := i + 1;
  281.      until (firstchar=SYN) or (firstchar=CAN) or (i=3);
  282.      if firstchar=CAN then goto 99;
  283.      if firstchar <> SYN then
  284.      begin
  285.           Wxmode := false;
  286.           i:=0;                                 {Try CRC xmodem 3 times}
  287.           writeln('Trying CRC Xmodem');
  288.           repeat
  289.                send(ord('C'));
  290.                firstchar := cgetc(4);           {4 seconds each}
  291.                if keypressed then
  292.                begin
  293.                     read(kbd,UserKey);
  294.                     if UserKey = ^X then goto 99
  295.                end;
  296.                i := i + 1;
  297.           until (firstchar=SOH) or (firstchar=CAN) or (i=3);
  298.           if firstchar = CAN then goto 99;
  299.           if firstchar <> SOH then
  300.           begin
  301.                Crcmode  := false;
  302.                Packetln := 129;                 {128 bytes + 1 byte Checksum}
  303.                i:=0;                            {Try Checksum xmodem 4 times}
  304.                writeln('Trying Checksum Xmodem');
  305.                repeat
  306.                     send(NAK);
  307.                     firstchar := cgetc(10);     {10 seconds each}
  308.                     if keypressed then
  309.                     begin
  310.                          read(kbd,UserKey);
  311.                          if UserKey = ^X then goto 99
  312.                     end;
  313.                     i := i + 1;
  314.                until (firstchar=SOH) or (firstchar=CAN) or (i=4);
  315.           end;                                  {Checksum}
  316.      end;                                       {CRC}
  317.      If wxmode then status(2,'RECV WXmodem');
  318.         { firstchar contains the first character and Wxmode and Crcmode
  319.           indicate the type of Xmodem }
  320.  
  321.      prevchar := firstchar;                     {save the firstchar}
  322.      while (EotFlag = false) and (Errors < MAXERRS) do
  323.      begin                                      {locate start of packet}
  324.        if (firstchar=SOH) and
  325.           ((Wxmode and (prevchar=SYN)) or (not Wxmode)) then
  326.        begin                                    {process packet}
  327.           prevchar := -1;
  328.           firstchar := -1;
  329.           sectcurr := dlecgetc(15);
  330.           sectcomp := dlecgetc(15);
  331.           if sectcurr = (sectcomp xor 255) then
  332.           begin                                 {sequence versus compl good}
  333.                if sectcurr = ((sectnum + 1) and 255) then
  334.                begin                            {in sequence}
  335.                     crcval   := 0;
  336.                     checksum := 0;
  337.                     j        := 1;
  338.                     repeat
  339.                          firstchar := dlecgetc(15);
  340.                          if firstchar >= 0 then
  341.                          begin
  342.                               if j < 129 then
  343.                                  dbuffer[bufcurr,j] := firstchar;
  344.                               if Crcmode then updcrc(firstchar)
  345.                               else checksum := (checksum and 255) + firstchar;
  346.                               j := j + 1;
  347.                          end;
  348.                     until (j > Packetln) or (firstchar < 0);
  349.                     if j > Packetln then        {good packet length}
  350.                     begin
  351.                          if (Crcmode and (crcval=0) or
  352.                          (not Crcmode and ((checksum shr 1) = firstchar)))
  353.                          then
  354.                          begin                  {good crc/checksum}
  355.                               firstchar := -1;  {make sure this byte not used
  356.                                                  for start of packet}
  357.                               errors  := 0;
  358.                               sectnum := sectcurr;
  359.                               blkcnt  := blkcnt + 1;
  360.                               send(ACK);
  361.                               if Wxmode then send(sectcurr and 3);
  362.                               ShowCrt(blkcnt, errors, toterr);
  363.                               bufcurr := bufcurr + 1;
  364.                               if bufcurr > bufnum then
  365.                               begin             {Disk write routine}
  366.                                    bufcurr := 1;
  367.                                    BlockWrite(blkfile,dbuffer,bufnum,bresult);
  368.                                    if bresult <> bufnum then
  369.                                    begin
  370.                                         writeln ('Disk write error');
  371.                                         goto 99;
  372.                                    end;
  373.                               end;              {End of disk write routine}
  374.                          end                    {good crc/checksum}
  375.                          else
  376.                          begin                  {bad crc/checksum}
  377.                               writeln('CRC/Checksum error',(blkcnt+1):6);
  378.                               errors := errors + 1;
  379.                               toterr := toterr + 1;
  380.                               send(NAK);
  381.                               if wxmode then
  382.                               begin
  383.                                    send(sectcurr and 3);
  384.                                    lignore := maxwindow;
  385.                               end;
  386.                          end;                   {bad crc/checsum}
  387.                     end                         {good packet length}
  388.                     else
  389.                     begin                       {bad packet length}
  390.                          writeln('Short block error',(blkcnt+1):6);
  391.                          errors := errors + 1;
  392.                          toterr := toterr + 1;
  393.                          send(NAK);
  394.                          if wxmode then
  395.                          begin
  396.                               send(sectcurr and 3);
  397.                               lignore := maxwindow;
  398.                          end;
  399.                     end;                        {bad packet length}
  400.                end                              {good block sequence number}
  401.                else
  402.                begin                            {invalid sequence number}
  403.                     if lignore <= 0 then        {are we ignoring packets?}
  404.                     begin
  405.                          writeln('Out of sequence error',(blkcnt+1):6);
  406.                          errors := errors + 1;
  407.                          toterr := toterr + 1;
  408.                          send(NAK);
  409.                          if wxmode then
  410.                          begin
  411.                               send((sectnum+1) and 3);
  412.                               lignore := Maxwindow;
  413.                          end;
  414.                     end
  415.                     else lignore := lignore -1
  416.                end;                             {invalid sequence number}
  417.           end                                   {valid complement}
  418.           else
  419.           begin                                 {invalid complement}
  420.                writeln('Packet sequence complement error',(blkcnt+1):6);
  421.                writeln('sectcurr=',sectcurr:5,'sectcomp=',sectcomp:5);
  422.                errors := errors + 1;
  423.                toterr := toterr + 1;
  424.                send(NAK);
  425.                if wxmode then
  426.                begin
  427.                     send((sectnum+1) and 3);
  428.                     lignore := Maxwindow;
  429.                end;
  430.           end;                                  {invalid complement}
  431.        end                                      {process packet}
  432.        else                                     {not start of packet}
  433.        begin
  434.             case prevchar of
  435.               EOT:   begin
  436.                           if firstchar=EOT then
  437.                           begin
  438.                                EotFlag := True;
  439.                                send(ACK);
  440.                           end;
  441.                      end;
  442.               CAN:   begin
  443.                           if firstchar=CAN then
  444.                           goto 99;
  445.                      end;
  446.             end;                                {Of case}
  447.             if not EotFlag then
  448.             begin
  449.                  if firstchar=EOT then send(NAK);  {first EOT received}
  450.                  prevchar := firstchar;
  451.                  firstchar := cgetc(15);        {start of packet!!!!}
  452.                  if firstchar=-1 then
  453.                  begin
  454.                       if (prevchar=CAN) or (prevchar=EOT) then
  455.                          firstchar := prevchar  {assume two have been received}
  456.                       else
  457.                       begin
  458.                            writeln('Timeout on start of packet');
  459.                            errors := errors + 1;
  460.                            toterr := toterr + 1;
  461.                            send(NAK);
  462.                            if wxmode then
  463.                            begin
  464.                                 send((sectnum+1) and 3);
  465.                                 lignore := Maxwindow;
  466.                            end;
  467.                       end;
  468.                  end;                           {Timeout at start of packet}
  469.                  if keypressed then
  470.                  begin
  471.                       read(kbd,UserKey);
  472.                       if UserKey = ^X then goto 99;
  473.                       end;
  474.             end;                                {end of not EotFlag}
  475.          end;                                   {not start of packet}
  476.      end;                                       {xmodem loop}
  477.            {If there are any xmodem packets left in dbuffer, we had best
  478.             write them out}
  479.  
  480.      If EotFlag and (bufcurr>1) then
  481.      begin
  482.           bufcurr := bufcurr - 1;
  483.           writeln ('Writing final blocks',bufcurr:3);
  484.           BlockWrite(Blkfile,dbuffer,bufcurr,bresult);
  485.           {$I-} close(blkfile) {$I+};
  486.           i := IOresult;                     {clear ioresult}
  487.           if bufcurr <> bresult then
  488.           begin
  489.                writeln('Disk write error at end of receive');
  490.                EotFlag := False;                {no longer a 'real' eot}
  491.           end;
  492.      end;
  493.  
  494.   99:
  495.      if not Eotflag then
  496.      begin
  497.           if errors >= Maxerrs then
  498.                writeln('Maximum errors exceeded')
  499.           else
  500.           if UserKey = ^X then
  501.                writeln('^X entered');
  502.           if firstchar = CAN then
  503.                writeln('Cancel received')
  504.           else
  505.           begin
  506.                send(CAN); send(CAN);
  507.                purge;
  508.           end;
  509.           if openflag then
  510.           begin
  511.                {$I-} close(blkfile) {$I+};
  512.                i := IOresult;                     {clear ioresult}
  513.               {USED TO BE A PURGE HERE}
  514.  
  515.           end;
  516.      end;
  517.      status(0,' ');
  518.      status(2,'On-Line/Ready');
  519.      dbits        := db;
  520.      parity       := p;
  521.      stop_bits    := sb;
  522.      update_uart;
  523. end;
  524.  
  525.  
  526. procedure send_wcp;
  527. {Checksum Xmodem, CRC Xmodem, WXmodem transmit routine}
  528.  
  529. Label
  530.   tran,99;
  531. Var
  532.    UserKey : char;
  533.    c, i, j, sectnum, errors   : integer;
  534.    tblks, sblks, ackblks, rblks : integer;        {total, sent, ack'd blocks}
  535.    twindow, awindow           : integer;          {transmission window}
  536.    bresult, nblks, prevchar   : integer;
  537.    bflag, canflag, xpause     : boolean;
  538.    blkfile                    : file;
  539.    statstr                    : bigstring;
  540.    xblk, ackseq               : integer;
  541.    trfile                     : text;
  542.  
  543. procedure checkack(tlimit : integer);
  544.  
  545. var
  546. inchar  :   integer;
  547.  
  548. begin
  549.    repeat                                      {until no more data & timelimit}
  550.       inchar := cgetc(0);
  551.       if inchar <> -1 then
  552.       begin                                     {got a character}
  553.          if wxmode then                         {wxmodem}
  554.          begin
  555. {            write(trfile,inchar:4);   }
  556.             case inchar of
  557.                XOFF : begin
  558.                          xpause := true;
  559.                          txwindow(8,'Received - waiting');
  560.                       end;
  561.                XON  : begin
  562.                          xpause := false;
  563.                          txwindow(8,'No');
  564.                       end;
  565.                ACK, NAK, CAN :
  566.                       prevchar := inchar;       {save ACK/NAK/CAN}
  567.                0..3 : begin                     {valid ACK/NAK sequence number}
  568.                          case prevchar of
  569.                             ACK : begin
  570.                                      ackseq := inchar - (ackblks and twindow);
  571.                                      if ackseq <= 0 then
  572.                                         ackseq := ackseq + maxwindow;
  573.                                      nblks := ackblks + ackseq;
  574.                                      if nblks <= sblks then
  575.                                      begin
  576.                                         ackblks := nblks;
  577.                                         str(ackblks:4,statstr);
  578.                                         txwindow(6,statstr);
  579.                                         if errors <> 0 then
  580.                                         begin
  581.                                            errors := 0;
  582.                                            txwindow(10,'0');
  583.                                         end;
  584.                                      end;
  585. {                                     writeln(trfile,' ACK ',inchar:2,ackblks:5);}
  586.                                      prevchar := -1;
  587.                                   end;                 {case ACK}
  588.                             NAK : begin
  589.                                      ackseq := inchar - (ackblks and twindow);
  590.                                      if ackseq <= 0 then
  591.                                         ackseq := ackseq + maxwindow;
  592.                                      nblks := ackblks + ackseq;
  593.                                      if nblks <= sblks then
  594.                                      begin
  595.                                         sblks := nblks - 1;
  596.                                         if (sblks - ackblks) <= 2 then
  597.                                            ackblks := sblks;
  598.                                         str(nblks:4,statstr);
  599.                                         txwindow(7,statstr);
  600.                                         str(sblks:4,statstr);
  601.                                         txwindow(5,statstr);
  602.                                         errors := errors + 1;
  603.                                         str(errors:3,statstr);
  604.                                         txwindow(10,statstr);
  605.                                      end
  606.                                      else
  607.                                      begin
  608.                                        GotoXY(3,12);
  609.                                        ClrEol;
  610.                                        writeln('Invalid NAK seq ',nblks:4,ackseq:4,inchar:3);
  611.                                      end;
  612. {                                    writeln(trfile,' NAK ',inchar:2,ackblks:5,sblks:5);}
  613.                                      prevchar := -1;
  614.                                   end;                 {case NAK}
  615.                             CAN : begin
  616.                                      if inchar = CAN then
  617.                                         canflag := true;
  618.                                   end;
  619.                          end;                          {of case prevchar}
  620.                       end;                             {case 0..3}
  621.                else                                    {of case inchar}
  622.                   prevchar := -1;       {inchar not XON/XOFF/ACK/NAK/CAN/0/1/2/3}
  623.             end;                                {of case inchar}
  624.          end                                    {wxmodem mode}
  625.          else
  626.          begin                                  {regular xmodem}
  627.             case inchar of
  628.                ACK : begin
  629.                         ackblks := ackblks + 1;
  630.                         errors  := 0;
  631.                      end;
  632.                NAK : begin
  633.                         sblks   := sblks - 1;
  634.                         errors  := errors + 1;
  635.                      end;
  636.                CAN : begin
  637.                         if prevchar = CAN then
  638.                            canflag := true;
  639.                         prevchar   := CAN;
  640.                      end;
  641.             else     prevchar := inchar;
  642.             end;                                {end of case inchar}
  643.          end;                                   {regular xmodem}
  644.       end                                       {end of got a character}
  645.       else                                      {no incoming data, inchar=-1}
  646.       begin
  647.          if tlimit > 0 then
  648.          begin
  649.             delay(1);
  650.             tlimit := tlimit - 1;
  651.          end;
  652.       end;                                      {end no incoming data}
  653.       if keypressed then
  654.       begin
  655.          read(kbd,UserKey);
  656.          if UserKey = ^X then
  657.          begin
  658.             canflag := true;
  659.             tlimit  := 0;                       {force end of repeat}
  660.             inchar  := -1;                      { "    "   "  "     }
  661.             xpause  := false;
  662.             purge;
  663.          end;
  664.       end;                                      {end of keypressed}
  665.    until (tlimit <= 0) and (inchar = -1);       {repeat until nothing left}
  666. end;                                            {of procedure checkack}
  667.  
  668. procedure dlesend(c:integer);
  669. begin
  670.    if wxmode then
  671.    begin
  672.       if buf_start <> buf_end then              {if there is any incoming data}
  673.          checkack(0);
  674.       while xpause do
  675.          checkack(25);
  676.       case c of
  677.          SYN, XON, XOFF, DLE :  begin
  678.                                    send(DLE);
  679.                                    send(c xor 64);
  680.                                 end;
  681.                             else   send(c);
  682.       end;
  683.    end
  684.    else send(c);                                {regular xmodem}
  685. end;
  686.  
  687.  
  688. begin
  689.      status(2, 'SEND XMODEM');
  690.      SaveCommStatus;
  691.      openflag := false;
  692. {    assign(trfile,'trace');}
  693. {    rewrite(trfile);}
  694.      OpenTemp(1,3,80,8,2);
  695.      repeat
  696.        write('Enter a filename for upload file (<cr> to abort): ');
  697.        readln(fname);
  698.        supcase(fname);
  699.        if length(fname) > 0 then
  700.        begin
  701.          bflag := exists(fname);
  702.          if not bflag then
  703.          begin
  704.            writeln('Could not open file ',fname);
  705.            writeln('(Spelling or drive designation wrong?)');
  706.            writeln
  707.          end
  708.        end
  709.     until bflag or (length(fname) = 0);
  710.     CloseTemp;
  711.     if length(fname) = 0 then
  712.       goto 99;
  713.     Assign(Blkfile,fname);
  714.     {I-} Reset(Blkfile); {I+}
  715.     If IOresult <> 0 then
  716.        goto 99;
  717.     openflag := true;
  718.     txwindow(1,fname);
  719.     tblks := Trunc(LongFileSize(Blkfile));
  720.     str((tblks)*22.3333333/speed:6:2,statstr);
  721.     txwindow(3,statstr);
  722.     str(tblks:4,statstr);
  723.     txwindow(4,statstr);
  724.     txwindow(12,'Press ^X to abort transfer');
  725.     prevchar := -1;
  726.     sblks   := 0;                               {sent blks}
  727.     ackblks := 0;                               {ack'd blocks}
  728.     rblks   := 0;                               {highest read block}
  729.     errors  := 0;
  730.     canflag := false;                           {not cancelled yet}
  731.     xpause  := false;
  732.     UserKey := #0;
  733.  
  734.                       {Xmodem transmit protocol initialization}
  735.  
  736.     i := 0;
  737.     repeat
  738.       c := cgetc(1);
  739.       if c <> -1 then
  740.       begin                                     {we got a character!}
  741.            i := i + 1;                          {one of our 10 characters}
  742.            case c of
  743.              NAK   :  begin                     {Checksum Xmodem}
  744.                            crcmode := false;
  745.                            wxmode  := false;
  746.                            twindow := 0;
  747.                            txwindow(2,'Checksum Xmodem Send');
  748.                            goto tran;
  749.                       end;
  750.              CHARC :  begin                     {CRC Xmodem}
  751.                            crcmode := true;
  752.                            wxmode  := false;
  753.                            twindow := 0;
  754.                            txwindow(2,'CRC Xmodem Send');
  755.                            goto tran;
  756.                       end;
  757.              CHARW :  begin                     {WXmodem}
  758.                            crcmode := true;
  759.                            wxmode  := true;
  760.                            twindow := Maxwindow - 1;
  761.                            txwindow(2,'WXmodem Send');
  762.                            str(Maxwindow:1,statstr);
  763.                            txwindow(9,statstr);
  764.                            goto tran;
  765.                       end;
  766.              CAN   :  begin                     {Cancel request received}
  767.                            if canflag then goto 99
  768.                            else canflag := true;
  769.                       end;
  770.            end;                                 {of case c}
  771.        end;                                     {got a character}
  772.  
  773.        if keypressed then read(kbd, UserKey);
  774.     until (i > 10) or (UserKey = ^X);
  775.     if UserKey = ^X then goto 99;
  776.     UserKey := #0;
  777.     txwindow(10,'Could not start: cancelled');
  778.     purge;
  779.     goto 99;
  780.  
  781. tran:                                           {let's send the file!}
  782.     awindow := twindow;
  783.     errors  := 0;
  784.               {Xmodem packet level loop}
  785.  
  786.     while (ackblks < tblks) and (errors <= MAXERRS) do
  787.     begin
  788.        i := 0;
  789.        while (sblks - ackblks) > awindow do     {is the ack window open?}
  790.        begin                                    {no, so wait for ack/nak}
  791.           i := i + 1;
  792.           if i <= 1 then
  793.           begin
  794.              str((awindow+1):1,statstr);
  795.              txwindow(9,concat(statstr,' Closed'));
  796.           end;
  797.           checkack(50);                         {50*2400 = 120 seconds +}
  798.           if canflag then
  799.              goto 99;
  800.           if keypressed then
  801.           begin
  802.              read(kbd,UserKey);
  803.              if UserKey = ^X then
  804.                 goto 99;
  805.           end;                               {of keypressed}
  806.           if i > 2400 then
  807.           begin
  808.              txwindow(11,'Timeout for ack');
  809.              sblks := ackblks + 1;
  810.              if sblks > tblks then
  811.                 goto 99;
  812.           end;
  813.           if (sblks - ackblks) <= awindow then
  814.           begin
  815.              str((awindow+1):1,statstr);
  816.              txwindow(9,statstr);
  817.           end;
  818.        end;                                     {window closed}
  819.  
  820.        if sblks < tblks then                    {is there anything left?}
  821.        begin
  822.           awindow := twindow;                   {ack window is transmit window}
  823.                            {disk read routine}
  824.           sblks := sblks + 1;
  825.           xblk  := sblks;
  826.           while (xblk > rblks) or (xblk <= (rblks - bufnum)) do
  827.           begin
  828.              if xblk < (rblks - bufnum) then    {if we got nak'd back}
  829.              begin
  830.                 seek(blkfile,(xblk-1));
  831.              end;
  832.              BlockRead(blkfile,dbuffer,bufnum,bresult);
  833.              rblks := xblk + bufnum - 1;        {note rblks must go past eof}
  834.           end;                                  {end of disk read routine}
  835.  
  836.           j := bufnum - rblks + xblk;           {index of next packet}
  837.  
  838.           crcval := 0;
  839.           checksum := 0;
  840.           str(xblk:4,statstr);
  841.           txwindow(5,statstr);
  842.           if wxmode then
  843.           begin
  844.              while xpause do
  845.                 checkack(0);
  846.              send(SYN);
  847.           end;
  848.           dlesend(SOH);
  849.           dlesend(xblk and 255);                {block sequence}
  850.           dlesend((xblk and 255) xor 255);      {complement sequence}
  851.           for i := 1 to 128 do
  852.           begin
  853.              c := dbuffer[j,i];
  854.              if crcmode then updcrc(c)
  855.              else checksum := (checksum + c) and 255;
  856.              dlesend(c);
  857.           end;
  858.           if crcmode then
  859.           begin
  860.              dlesend(hi(crcval));
  861.              dlesend(lo(crcval));
  862.           end
  863.           else
  864.              send(checksum);
  865.           if canflag then
  866.              goto 99;
  867. {         writeln(trfile,'SENT ',sblks:5,xblk:5);}
  868.        end                                      {something to send}
  869.        else
  870.        begin                                    {nothing else to send}
  871.           if wxmode then
  872.           begin
  873.              awindow := sblks - ackblks - 1;    {wait for final acks}
  874.              str(awindow:1,statstr);
  875.              txwindow(9,concat(statstr,' -- Closing'));
  876.           end;
  877.        end;
  878.     end;                                        {xmodem send routine}
  879.  
  880.     repeat                                      {end of transmission}
  881.       send(EOT);
  882.       UserKey := #0;
  883.       repeat
  884.         c := cgetc(15);
  885.         if KeyPressed then read(kbd,UserKey);
  886.       until (c <> -1) or (UserKey = ^X);
  887.  
  888.       if UserKey = ^X then goto 99;
  889.       if c = NAK then
  890.       begin
  891.          errors := errors + 1;
  892.          delay(250);
  893.       end;
  894.     until (c = ACK) or (errors = MAXERRS);
  895.     if errors = MAXERRS then
  896.        txwindow(11,'ACK not received at EOT');
  897.     99:
  898. {   close(trfile);}
  899.     if openflag then
  900.     begin
  901.        {$I-} close(blkfile) {$I+} ;
  902.        i := IOresult;                           {clear ioresult}
  903.     end;
  904.     if ((UserKey = ^X) or canflag) and (length(fname) > 0) then
  905.     begin
  906.       txwindow(11,'Cancel-at your request');
  907.       repeat
  908.         send(CAN);
  909.         send(CAN);
  910.         purge
  911.       until cgetc(1) = -1
  912.     end;
  913.     txwindow(12,'Press any key to continue');
  914.     repeat
  915.     until (keypressed);
  916.     txwindow(99,'  ');
  917.     status(2,'On-Line/Ready');
  918.     status(3,' ');
  919.     dbits        := db;
  920.     parity       := p;
  921.     stop_bits    := sb;
  922.     update_uart
  923. end;
  924.